import Vue from 'vue';
import lodash from 'lodash';
import moment from 'moment';
import streamSaver from "streamsaver";
import { MessageBox, Message } from 'element-ui';

let messageInstance = null;

/**
 * 时间日期格式化全局引入
 * 使用文档：https://momentjs.bootcss.com/
 */
export function $moment() {
	return moment();
}

/**
 * 生成不重复随机数
 * len 配置生成的随机数长度
 */
export function $genUUID() {
	let s = [];
	let hexDigits = "0123456789abcdef";
	for (let i = 0; i < 32; i++) {
		s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
	}
	s[14] = "4";
	s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
	s[8] = s[13] = s[18] = s[23];
	return s.join("");
}

/**
 * 判断是否JSON
 * str 判断字符串
 */
export function $isJSON(str) {
	try {
		JSON.parse(str);
		return true;
	} catch (error) {
		return false;
	}
}

/**
 * 获取文件后缀名
 * file 文件路径地址
 */
export function $getFileSuffix(filename) {
	if (typeof filename !== 'string' || filename === '') return '';
	// 处理以点开头且无其他点的文件（如 .gitignore）
	if (/^\.[^.]+$/.test(filename)) {
		return filename.slice(1);
	}
	// 使用正则表达式匹配最后一个点后的扩展名
	const match = filename.match(/(?:\.([^./\\]*))?$/);
	return match && match[1] ? match[1] : '';
}

/**
 * 获取对象指定属性
 * object 取值对象源数据
 * arr 需要获取的属性数组
 */
export function $getAssignAttr(object, arr) {
	let obj = {};
	Array.from(arr).forEach(item => {
		obj[item] = object[item];
	})
	return obj;
}

/**
 * 数组转对象
 * arr 需要转换的数组
 * code 转换后对象属性名取值
 * value 转换后对象属性对应数据
 */
export function $arrayToObject(arr, code, value) {
	let obj = {};
	Array.from(arr).forEach(item => {
		obj[item[code || 'code']] = item[value || 'value'];
	})
	return obj;
}

/**
 * 深度复制数据
 * params 需要复制的数据
 */
export function $deepClone(params) {
	return JSON.parse(JSON.stringify(params));
}

/**
 * 去除字符串中的空格
 * str 需要处理的字符串
 * type 去除字符串空格的类型
 */
export function $wipeSpace(str, type) {
	switch (type) {
		case 'all':
			return str.replace(/\s*/g, "");
			break;
		case 'both':
			return str.replace(/^\s|\s$/g, "");
			break;
		case 'innerLeft':
			return str.replace(/^\s*/, "");
			break;
		case 'innerRight':
			return str.replace(/(\s*$)/g, "");
			break;
	}
}

/**
 * 清除html中的行内样式
 * html 需要处理的数据
 */
export function $cleanStyle(html) {
	let newHtml = ''
	let reg = /style\s*?=\s*?(['"])[\s\S]*?\1/;
	if (html) {
		newHtml = html.replace(reg, '')
	}
	return newHtml
}

/**
 * 清除字符串中的html标签
 * html 需要处理的数据
 */
export function $cleanHtmlTag(htmlStr) {
	let text = ''
	let reg = /<[^>]+>/g;
	if (htmlStr) {
		text = htmlStr.replace(reg, '')
	}
	return text;
}

/**
 * 清除HTML字符串中的空标签和只存在&nbsp;的标签
 * html 需要处理的数据
 */
export function $removeEmptyTag(html) {
	let previous;
	let result = html;
	let regex = new RegExp('<([^>]+?)>(?:\\s| |<br\\s*/?>)*</\\1>', 'gi');
	do {
		previous = result;
		result = result.replace(regex, '');
	} while (previous !== result);
	return result;
}

/**
 * 添加树节点
 * tree 树形数据
 * id 指定数据id
 * obj 需要添加的节点
 * type 插入到节点之前获取之后
 */
export function $appendNodeInTree(tree, id, obj, type) {
	let findParentNode = (data, callback) => {
		for (let i = 0; i < data.length; i++) {
			if (data[i].id == id) {
				callback(data, i);
				break;
			}
			if (data[i].children && data[i].children.length > 0) {
				findParentNode(data[i].children, callback)
			}
		}
	}
	let findNode = (data, callback) => {
		for (let i = 0; i < data.length; i++) {
			if (data[i].id == id) {
				callback(data[i]);
				break;
			}
			if (data[i].children && data[i].children.length > 0) {
				findNode(data[i].children, callback)
			}
		}
	};
	if (!id) {
		tree.push(obj);
		return false;
	}
	if (!type) {
		findNode(tree, node => {
			!node.children && Vue.set(node, 'children', []);
			node.children.push(obj);
		})
		return false;
	}
	findParentNode(tree, (list, index) => {
		switch (type) {
			case 'active_top':
				list.splice(index, 0, obj);
				break;
			case 'active_right':
				list.splice(index + 1, 0, obj);
				break;
			case 'active_bottom':
				list.splice(index + 1, 0, obj);
				break;
			case 'active_left':
				list.splice(index, 0, obj);
				break;
		}
	})
}

/**
 * 删除树节点
 * tree 树形数据
 * id 指定数据id
 */
export function $removeNodeInTree(tree, id, children = "children") {
	if (!tree || !tree.length) return;
	for (let i = 0; i < tree.length; i++) {
		if (tree[i].id === id) {
			tree.splice(i, 1);
			break;
		}
		this.$removeNodeInTree(tree[i][children], id)
	}
}

/**
 * 修改树节点
 * tree 树形数据
 * id 指定数据id
 */
export function $updateNodeInTree(tree, id, obj) {
	if (!tree || !tree.length) return;
	for (let i = 0; i < tree.length; i++) {
		if (tree[i].id == id) {
			tree[i] = obj;
			break;
		}
		this.$updateNodeInTree(tree[i].children, id, obj);
	}
}

/**
 * 查找树指定节点
 * tree 树形数据
 * id 指定数据id
 */
export function $findNodeInTree(tree, id, children = 'children') {
	let obj = {};
	const findNode = (tree, callback) => {
		for (let i = 0; i < tree.length; i++) {
			if (tree[i].id == id) {
				callback(tree[i]);
				break;
			}
			if (tree[i][children] && tree[i][children].length > 0) {
				findNode(tree[i][children], callback)
			}
		}
	};
	findNode(tree, data => {
		obj = {
			...data
		}
	})
	return obj;
}

/**
 * 移动树指定节点
 * tree 树形数据
 * id 目标节点id
 * obj 当前移动的节点
 * type 插入到节点之前获取之后
 * flag 判断是否可以移入
 */
export function $moveNodeInTree(tree, id, obj, type, flag = false) {
	if (!id) {
		this.$removeNodeInTree(tree, obj.id);
		tree.push(obj);
	} else if (flag && obj.id !== id) {
		this.$removeNodeInTree(tree, obj.id);
		this.$appendNodeInTree(tree, id, obj, type);
	}
}

/**
 * 判断是否是树形结构子节点
 * tree 树形数据
 * id 指定节点id
 */
export function $judgeIsTreeSonNode(tree, id) {
	let node = this.$findNodeInTree(tree, id);
	return Object.keys(node).length > 0;
}

/**
 * 树形数据转扁平化数据
 * dataSource 树形源数据
 */
export function $treeToFlat(dataSource, children = 'children') {
	let flatArr = [];
	let dataArr = this.$deepClone(dataSource);
	const flatFunction = arr => {
		Array.from(arr).forEach(item => {
			flatArr.push(item);
			!!item[children] && item[children].length > 0 ? flatFunction(item[children]) : null;
			delete item[children];
		})
	}
	flatFunction(dataArr);
	return flatArr;
}

/**
 * 扁平数据转树形数据
 * dataSource 扁平化源数据
 * parentId 树形数据顶级ID
 */
export function $flatToTree(dataSource, parentId, parentValue) {
	let dataArr = this.$deepClone(dataSource);
	let treeList = dataArr.reduce((prev, cur) => {
		prev[cur["id"]] = cur;
		return prev;
	}, {});
	let result = dataArr.reduce((prev, cur) => {
		let pid = cur[parentId];
		let parent = treeList[pid];
		if (parent) {
			parent.children ? parent.children.push(cur) : (parent.children = [cur]);
		} else if (pid == (parentValue || 0)) {
			prev.push(cur);
		}
		return prev;
	}, []);
	return result;
}

/**
 * 树形数据添加排序
 * tree 树形数据
 */
export function $treeAddedSort(tree, children = 'children', idx = '') {
	return tree.map((item, index) => {
		let obj = { ...item };
		obj['sort'] = parseInt(`${idx}${index + 1}`);
		obj[children] = item[children] || [];
		obj[children] = obj[children].length > 0 && this.$treeAddedSort(obj[children], children, obj['sort']);
		return obj;
	});
}

/**
 * 判断是否为对象
 * obj 传入的对象
 */
export function $isPlainObject(obj) {
	return typeof obj === 'object' && Object.prototype.toString.call(obj) === '[object Object]';
}

/**
 * 判断对象是否相等
 * aObj/bObj 为需要对比的两个对象
 */
export function $isEqualObject(aObj, bObj) {
	if (!this.$isPlainObject(aObj) || !this.$isPlainObject(bObj)) {
		return aObj === bObj
	}
	if (Object.keys(aObj).length !== Object.keys(bObj).length) {
		return false
	}
	for (let key in aObj) {
		if (this.$isPlainObject(aObj[key]) && this.$isPlainObject(bObj[key])) {
			if (!this.$isEqualObject(aObj[key], bObj[key])) {
				return false;
			}
		} else if (aObj[key] !== bObj[key]) {
			return false;
		}
	}
	return true;
}

/**
 * 判断数组是否相等
 * aArr/bArr 为需要对比的两个数组
 */
export function $isEqualArray(aArr, bArr) {
	if (!Array.isArray(aArr) || !Array.isArray(bArr)) return false;
	if (aArr.length !== bArr.length) return false;
	for (let i = 0; i < aArr.length; i++) {
		if (this.$isPlainObject(aArr[i]) && this.$isPlainObject(bArr[i])) {
			if (!this.$isEqualObject(aArr[i], bArr[i])) {
				return false;
			}
		} else if (aArr[i] !== bArr[i]) {
			return false;
		}
	}
	return true;
}

/**
 * 判断对象是否是嵌套对象
 * obj 需要判断的对象
 */
export function $isNestedObject(obj) {
	if (!this.$isPlainObject(obj)) return false;
	for (let key in obj) {
		if (obj[key] instanceof Object) {
			if (obj[key] instanceof Array && obj[key].length > 0) {
				return true;
			} else if (this.$isPlainObject(obj[key])) {
				return true;
			}
		}
	}
	return false;
}

/**
 * 请求参数JSON转query
 * params 传入的JSON参数
 */
export function $objectToQuery(params) {
	let argArr = Object.entries(!params ? {} : params);
	return argArr.reduce((cur, arr, index) => {
		let symbol = cur.length === 0 ? '?' : '&';
		switch (typeof arr[1]) {
			case 'number':
				cur += isNaN(arr[1]) ? '' : `${symbol}${arr[0]}=${arr[1]}`;
				break;
			case 'string':
				cur += `${symbol}${arr[0]}=${arr[1]}`;
				break;
			case 'boolean':
				cur += `${symbol}${arr[0]}=${arr[1]}`;
				break;
		}
		return cur;
	}, '');
}

/**
 * 全屏加载动画
 */
export function $fullLoading() {
	let that = this;
	return {
		loading: null,
		open() {
			this.loading = that.$loading({
				lock: true,
				text: "正在加载...",
				spinner: "el-icon-loading",
				background: "rgba(0, 0, 0, 0.6)",
				customClass: 'custom_full_loading'
			});
			return this;
		},
		close() {
			this.loading.close();
			this.loading = null;
			return this;
		}
	}
}

/**
 * 截取带HTML样式的字符串，并保留并自动补齐HTML标签
 * html: 将要截取的HTML字符串
 * nlen: 截取后的长度，包含标签之间的空格
 * isByte: 是否按照字节长度截取
 */
export function $substringHtml(html, nlen, isByte = false) {
	const getPlainText = (str) => {
		let text = str.replace(/<[^<^>]*>/g, "");
		let tlen = isByte ? text.replace(/[^\x00-\xff]/g, "**").length : text.length;
		if (/^\d+$/.test(nlen) && tlen >= nlen) {
			return text;
		} else {
			return false
		}
	}
	const getTagsData = (str) => {
		let index = 0;
		let reg = /<[^<^>]+>/;
		let matchs = new Array();
		while (reg.test(str)) {
			let obj = new Object();
			let len = str.search(/<[^<^>]+>/) + str.match(/<[^<^>]+>/)[0].length;
			obj['index'] = index + str.search(reg);
			obj['string'] = str.match(reg).toString();
			str = str.substr(len);
			index += len;
			matchs.push(obj);
		}
		return matchs;
	}
	const getSliceHtml = (data) => {
		let sliceHtml = '';
		let startTags = [];
		let rgx1 = /<[^<^>^\/]+>/;
		let rgx2 = /<\/[^<^>^\/]+>/;
		let textStr = getPlainText(data);
		let matchArr = getTagsData(data);
		let selfTags = ['hr', 'br', 'img', 'input', 'meta'];
		if (!textStr) {
			return data;
		}
		if (isByte) {
			let i = 0;
			for (let z = 0; z < textStr.length; z++) {
				i += (textStr.charCodeAt(z) > 255) ? 2 : 1;
				if (i >= nlen) {
					sliceHtml = textStr.slice(0, (z + 1)) + '...';
					break;
				}
			}
		} else {
			sliceHtml = textStr.substr(0, nlen) + '...';
		}
		for (let i = 0; i < matchArr.length; i++) {
			if (sliceHtml.length <= matchArr[i].index) {
				matchArr = matchArr.slice(0, i);
				break;
			} else {
				sliceHtml = sliceHtml.substring(0, matchArr[i].index) + matchArr[i].string + sliceHtml.substr(
					matchArr[i].index);
				if (rgx1.test(matchArr[i].string.replace(/(\/\/)/g, ""))) {
					let name = matchArr[i].string.replace(/[<>]/g, "").split(" ");
					if (name.length > 0) {
						name = name[0];
						if (!selfTags.inArray(name)) {
							startTags.push(name);
						}
					}
				} else if (rgx2.test(matchArr[i].string)) {
					let name = matchArr[i].string.replace(/[<\/>]/g, "");
					if (startTags.length > 0 && startTags[startTags.length - 1] === name) {
						startTags.pop();
					}
				}
			}
		}
		if (startTags.length > 0) {
			for (var i = startTags.length - 1; i >= 0; i--) {
				sliceHtml += '</' + startTags[i] + '>';
			}
		}
		return sliceHtml;
	}
	if (typeof html !== 'string') return '';
	html = html.replace(/(^\s*)|(\s*$)/g, '').replace(/[\r\n]/g, '');
	return getSliceHtml(html);
}

/**
 * 文件下载
 * url: 下载文件的api地址
 * headers: 请求头配置
 */
export function $fileDownload(url, name, headers, callback) {
	return new Promise((resolve, reject) => {
		let fileBlob = '';
		fetch(url, { method: 'GET', cache: 'no-cache', headers }).then(async (res) => {
			let blobData = await res.blob();
			let elink = document.createElement("a");
			elink.download = name;
			elink.style.display = "none";
			elink.href = URL.createObjectURL(blobData);
			document.body.appendChild(elink);
			elink.click();
			document.body.removeChild(elink);
			if (!!callback && typeof callback === 'function') {
				callback();
			}
		})
	})
}

/**
 * 获取外部链接图片后缀
 * aArr,bArr 传入的的数组
 */
export function $getImageSuffix(url) {
	return new Promise((resolve, reject) => {
		fetch(url).then(res => res.blob()).then(res => {
			let fileReader = new FileReader();
			fileReader.onload = function(event) {
				let mimeType = event.target.result.split(";")[0].split(":")[1];
			};
			fileReader.readAsDataURL(res);
		})
	})
}

/**
 * 返回两个数组中不同元素的数组
 * aArr,bArr 传入的的数组
 */
export function $getArrDifference(aArr, bArr) {
	return [...aArr, ...bArr].filter((item, index, arr) => {
		return arr.indexOf(item) === arr.lastIndexOf(item);
	})
}

/**
 * 返回两个数组中相同元素的数组
 * aArr,bArr 传入的数组
 */
export function $getArrIdentical(aArr, bArr) {
	return aArr.filter((item) => {
		return bArr.indexOf(item) !== -1;
	});
}

/**
 * 返回数字、英文、中文的顺序
 * aStr,bStr 传入的字符串
 */
export function $sortNumberEnZh(aStr, bStr, flag) {
	const isLetterOrNumberReg = (str) => /^[0-9a-zA-Z]+$/.test(str);
	const isAllChineseStr = (str) => /^[\u4E00-\u9FA5]+$/.test(str);
	const splitStringByNumber = (str, sortByNumericalSize = false) => {
		let strArr = [];
		const REG_STRING_NUMBER_PARTS = /\d+|\D+/g;
		const arr = str.match(REG_STRING_NUMBER_PARTS);
		for (let i = 0; i < arr.length; i++) {
			const splitStr = arr[i];
			if (isNaN(splitStr)) {
				strArr = strArr.concat(splitStr.split(''));
			} else {
				// Whether to split numbers
				if (!sortByNumericalSize) {
					strArr = strArr.concat(splitStr.split(''));
				} else {
					strArr.push(splitStr);
				}
			}
		}
		return strArr;
	};
	const compare = (a, b, sortByNumericalSize = true) => {
		// 都是数字或字母
		if (isLetterOrNumberReg(a) && isLetterOrNumberReg(b)) {
			return a.localeCompare(b, 'zh-Hans-CN', {
				numeric: true
			});
		}

		// 中文字符串自己比较
		if (isAllChineseStr(a) && isAllChineseStr(b)) {
			return a.localeCompare(b, 'zh-Hans-CN', {
				numeric: true
			});
		}

		const arrA = splitStringByNumber(a, sortByNumericalSize);
		const arrB = splitStringByNumber(b, sortByNumericalSize);

		let result = 0;
		const length = Math.min(arrA.length, arrB.length);
		for (let i = 0; i < length; i++) {
			const charA = arrA[i];
			const charB = arrB[i];
			// 数字, 字符串排在 中文前面
			if (!isAllChineseStr(charA) && isAllChineseStr(charB)) return -1;
			if (isAllChineseStr(charA) && !isAllChineseStr(charB)) return 1;
			// 中文字符直接比较
			if (isAllChineseStr(charA) && isAllChineseStr(charB)) {
				result = charA.localeCompare(charB, 'zh-Hans-CN');
			} else {
				// 都不是中文
				result = charA.localeCompare(charB, 'zh-Hans-CN', {
					numeric: true
				});
			}
			if (result !== 0) {
				return result;
			}
		}

		// result === 0;
		if (arrA.length > arrB.length) return 1;
		if (arrA.length < arrB.length) return -1;
		return 0;
	}
	return compare(aStr, bStr, flag)
}

export function $arrayMove(arr, old_index, new_index) {
	if (new_index >= arr.length) {
		var k = new_index - arr.length + 1;
		while (k--) {
			arr.push(undefined);
		}
	}
	arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
	return arr; // for testing
};

/**
 * 自定义提示组件
 * message: '提示语句'
 */
export function $showCustomMessage(type, message) {
	if (!!messageInstance) {
		Message.closeAll();
	}
	messageInstance = Message({
		type,
		message,
		onClose: () => {
			messageInstance = null;
		}
	})
}

/**
 * 详情返回数据
 * data: 详情数据
 */
export function $detailData(data) {
	return !!data && data.length > 0 && !!data[0] && Object.keys(data[0]) ? data[0] : {};
}

/**
 * 导出Excel文件
 */
/* export function $exportExcelFile(option) {
	return new Promise((resolve, reject) => {
		let opt = option || {};
		let xhr = new XMLHttpRequest();
		// FileSaver.saveAs(opt.url, '1680604017698_4062.png');
		xhr.open("get", opt.url, true);
		// xhr.responseType = "blob";
		xhr.onload = () => {
			if (xhr.status == 200) {
				console.log(xhr.response);
			}
		};
		xhr.send();
	})
} */

/**
 * 文件转BLOB临时路径
 * file: 文件
 */
export function $fileToBlob(file) {
	return new Promise((resolve, reject) => {
		let reader = new FileReader();
		reader.onload = function(event) {
			let blob = new Blob([event.target.result], { type: file.type });
			let blobURL = window.URL.createObjectURL(blob);
			resolve(blobURL);
		}
		reader.readAsArrayBuffer(file);
	})
}

/**
 * Base64转File
 * base64: Base64字符串
 * fileName: 文件名
 */
export function $base64ToFile(base64, fileName) {
	let arr = base64.split(','),
		mime = arr[0].match(/:(.*?);/)[1],
		bstr = atob(arr[1]),
		n = bstr.length,
		u8arr = new Uint8Array(n);
	while (n--) {
		u8arr[n] = bstr.charCodeAt(n);
	}
	return new File([u8arr], fileName, { type: mime });
}

/**
 * 判断是否深色系
 * hex: 色值
 */
export function isDarkColor(hex) {
	if (!/^#[0-9A-F]{6}$/i.test(hex)) {
		throw new Error('Invalid HEX color format');
	} else {
		let threshold = 128;
		let bigint = parseInt(hex.slice(1), 16);
		let r = (bigint >> 16) & 255;
		let g = (bigint >> 8) & 255;
		let b = bigint & 255;
		let brightness = 0.299 * r + 0.587 * g + 0.114 * b;
		return brightness < threshold;
	}
}

/**
 * 自动生成HEX颜色
 * number: 生成数量
 * type: 色系类型 dark / light / plain
 */
export function $genRandomHexColor(number, type = 'dark') {
	let colors = []; // 存放生成的颜色数组
	for (let i = 0; i < number; i++) {
		let hex = Math.floor(Math.random() * 16777216).toString(16);
		//生成ffffff以内16进制数
		while (hex.length < 6) {
			hex = "0" + hex; //while循环判断hex位数，少于6位前面加0凑够6位
		}
		let color = "#" + hex; // 将RGB值转换为字符串形式表示颜色
		if (!colors.includes(color)) {
			if (isDarkColor(color)) {
				colors.push(color);
			} else {
				i--;
			}
		} else {
			i--; // 若发现了重复颜色，则需要再次生成新的颜色
		}
	}
	return colors;
}

/**
 * 移除文件路径域名
 * url 文件路径
 */
export function $removeUrlDomain(url) {
	let reg = /^https?:\/\/[^\/]+/;
	let fileUrl = !url ? '' : url;
	return fileUrl.replace(reg, '').replace('/whu', '');
}